﻿#nullable enable

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Domain.Services;

namespace PHMEE.EnergyEfficiency;

public class LogAbstractDomainService : DomainService
{
    private readonly ILogAbstractRepository _logAbstractRepository;

    public LogAbstractDomainService(ILogAbstractRepository logAbstractRepository)
    {
        _logAbstractRepository = logAbstractRepository;
    }

    /// <summary>
    /// 获取 time 所在的 current voyage 的所有 log abstract
    /// </summary>
    /// <param name="vesselId"></param>
    /// <param name="time"></param>
    /// <returns></returns>
    public async Task<List<LogAbstract>> GetCurrentVoyageLogAbstracts(Guid vesselId, DateTime time)
    {
        // 查询最后一条 Departure 类的 log abstract
        var latestDeparture =
            await _logAbstractRepository.GetLatestDepartureTypeLogAbstractOrDefaultBeforeOrEqualTimeAsync(vesselId,
                time);
        if (latestDeparture == null)
        {
            return [];
        }

        // 查询最后一条 Departure 之后（不含）的所有 log abstract
        var logAbstracts = await _logAbstractRepository.GetListOpenClosedAsync(vesselId, latestDeparture.Ts, time);

        return logAbstracts;
    }

    /// <summary>
    /// 获取 logAbstracts 内的所有航次（包含 STS 航次），包含时间段前后不完整的航次 （“离港”到“离港”】是一个航次；
    /// StartTime 和 EndTime 可能是正午时间，也可能是整日时间（查年度时），均采用前开后闭区间
    /// TODO: 代码修改正确，可以正常编译后，需要编写单元测试来确保方法适合所有情况
    /// </summary>
    /// <param name="logAbstracts"></param>
    /// <param name="vesselId"></param>
    /// <returns></returns>
    public async Task<List<Voyage>> GetVoyagesAsync(List<LogAbstract> logAbstracts, Guid vesselId)
    {
        if (logAbstracts.Count == 0)
        {
            return [];
        }

        // 识别出所有航次
        // 首先，从 logAbstracts 按 Ts ASC 排序
        var sortedLogAbstracts = logAbstracts.OrderBy(la => la.Ts).ToList();

        var endTime = sortedLogAbstracts[^1].Ts;

        List<Voyage> voyages = [];
        Voyage? indexVoyage = null;

        // 然后按照“离港”到“离港”】的规则识别出航次中的 log abstracts，将识别出来的航次们转换成 Voyage 对象集合
        foreach (var logAbstract in sortedLogAbstracts)
        {
            // 当遇到离港事件时，开始一个新的航次
            if (LogAbstractEventTypeHelper.DepartureEvents.Contains(logAbstract.Event))
            {
                // 如果当前航次不为空，那么将其添加到航次列表中
                if (indexVoyage != null)
                {
                    indexVoyage.EndDepartureLogAbstract = logAbstract;
                    indexVoyage.LogAbstracts.Add(logAbstract);
                    indexVoyage.EndTime = logAbstract.Ts;
                    voyages.Add(indexVoyage);
                }

                // 开始新的航次
                indexVoyage = new Voyage
                {
                    StartDepartureLogAbstract = logAbstract,
                    StartTime = logAbstract.Ts,
                    // IsSTS = logAbstract.Event == LogAbstractEventType.DepartureSTS
                };
            }
            // 如果当前航次不为空，那么将日志添加到当前航次中
            else if (indexVoyage is not null)
            {
                indexVoyage.LogAbstracts.Add(logAbstract);
            }
            // 如果当前航次为空，那么创建一个新的航次
            else
            {
                // 获取当前航次的前一个离港类型的事件
                var previousDepartureLogAbstract =
                    await _logAbstractRepository.GetLatestDepartureTypeLogAbstractOrDefaultBeforeOrEqualTimeAsync(
                        vesselId, logAbstract.Ts);
                indexVoyage = new Voyage
                {
                    StartTime = logAbstract.Ts,
                    // IsSTS = previousDepartureLogAbstract?.Event == LogAbstractEventType.DepartureSTS
                };
                indexVoyage.LogAbstracts.Add(logAbstract);
            }
        }

        // 如果最后一个航次有日志，那么将其添加到航次列表中
        if (indexVoyage != null && indexVoyage.LogAbstracts.Count > 0)
        {
            indexVoyage.EndTime = endTime;
            voyages.Add(indexVoyage);
        }

        return voyages;
    }

    /// <summary>
    /// 获取时间段内的所有完整航次（包含 STS 航次），不包含时间段前后不完整的航次 （“离港”到“离港”】是一个航次；
    /// StartTime 和 EndTime 可能是正午时间，也可能是整日时间（查年度时），均采用前开后闭区间
    /// TODO: 代码修改正确，可以正常编译后，需要编写单元测试来确保方法适合所有情况
    /// </summary>
    /// <param name="logAbstracts"></param>
    /// <returns></returns>
    public static List<Voyage> GetCompleteVoyages(List<LogAbstract> logAbstracts)
    {
        if (logAbstracts.Count == 0)
        {
            return [];
        }

        // 识别出所有航次
        // 首先，从 logAbstracts 按 Ts ASC 排序
        var sortedLogAbstracts = logAbstracts.OrderBy(la => la.Ts).ToList();

        List<Voyage> voyages = [];
        Voyage? indexVoyage = null;

        // 然后按照“离港”到“离港”】的规则识别出航次中的 log abstracts，将识别出来的航次们转换成 Voyage 对象集合
        foreach (var logAbstract in sortedLogAbstracts)
        {
            // 当遇到离港事件时，开始一个新的航次
            if (LogAbstractEventTypeHelper.DepartureEvents.Contains(logAbstract.Event))
            {
                // 如果当前航次不为空，那么将其添加到航次列表中
                if (indexVoyage != null)
                {
                    indexVoyage.EndDepartureLogAbstract = logAbstract;
                    indexVoyage.LogAbstracts.Add(logAbstract);
                    indexVoyage.EndTime = logAbstract.Ts;
                    voyages.Add(indexVoyage);
                }

                // 开始新的航次
                indexVoyage = new Voyage
                {
                    StartDepartureLogAbstract = logAbstract,
                    StartTime = logAbstract.Ts,
                    // IsSTS = logAbstract.Event == LogAbstractEventType.DepartureSTS
                };
            }
            // 如果当前航次不为空，那么将日志添加到当前航次中
            else if (indexVoyage is not null)
            {
                indexVoyage.LogAbstracts.Add(logAbstract);
            }
        }

        // 如果最后一个航次已经完结，那么上面的 foreach 循环已经处理了，不需要再处理

        return voyages;
    }

    /// <summary>
    /// 获取时间段内所有完整的调整中的 log abstracts。
    /// 本方法旨在收集包括在给定船舶ID下的 (VoyageAdjustmentStart, VoyageAdjustmentOver] 时间段内的 log abstracts。
    /// 它确保仅返回完整的调整航段：即从 "VoyageAdjustmentStart" 开始到 "VoyageAdjustmentOver" 结束的记录。
    /// 如果一个调整航段开始了但尚未结束（存在 "VoyageAdjustmentStart" 但没有对应的 "VoyageAdjustmentOver"），则不包括该航段中的任何 logAbstract。
    /// 这要求对每个识别的调整段进行检查，确认其在输入的 log abstracts 中完整结束后才包含于返回结果。
    /// 若 logAbstracts 中没有明确的调整开始或结束事件，将通过查询数据库确认在指定时间前后的调整事件状态，从而决定是否返回记录。
    /// TODO: 需要单元测试
    /// </summary>
    /// <param name="logAbstracts">包含 logAbstract 记录的列表。</param>
    /// <param name="vesselId">用于查询数据库中调整记录状态的船舶ID。</param>
    /// <returns>返回一个列表，其中包含所有完整调整航段的 logAbstract 记录。如果没有完整的调整航段，则返回空列表。</returns>
    public async Task<List<LogAbstract>> GetAdjustedLogAbstractsByTimeRangeAsync(List<LogAbstract> logAbstracts,
        Guid vesselId)
    {
        if (logAbstracts.Count == 0)
        {
            return new List<LogAbstract>();
        }

        var sortedLogAbstracts = logAbstracts.OrderBy(la => la.Ts).ToList();
        DateTime startTime = sortedLogAbstracts[0].Ts;

        List<LogAbstract> adjustLogAbstracts = new List<LogAbstract>();

        // 检查 sortedLogAbstracts 中是否有 "VoyageAdjustmentStart" 或 "VoyageAdjustmentOver" 的记录
        if (!sortedLogAbstracts.Any(la =>
                la.Event == LogAbstractEventType.VoyageAdjustmentStart ||
                la.Event == LogAbstractEventType.VoyageAdjustmentOver))
        {
            var lastEventBeforeStartTime =
                await _logAbstractRepository.GetLatestAdjustLogAbstractOrDefaultBeforeOrEqualTimeAsync(vesselId,
                    startTime);
            if (lastEventBeforeStartTime?.Event == LogAbstractEventType.VoyageAdjustmentStart)
            {
                // 需要检查是否存在一个完结的调整段
                // 这里也可以继续用 startTime 来往后查，因为我们已经确定了 sortedLogAbstracts 中没有任何调整段的开始或结束标记
                var firstEventAfterStartTime =
                    await _logAbstractRepository.GetEarliestAdjustLogAbstractOrDefaultAfterTimeAsync(vesselId,
                        startTime);
                if (firstEventAfterStartTime?.Event == LogAbstractEventType.VoyageAdjustmentOver)
                {
                    return sortedLogAbstracts; // 只有当确认调整段完结后，才返回所有记录
                }

                return new List<LogAbstract>(0); // 如果调整段未完结，返回空列表
            }
            else
            {
                return new List<LogAbstract>(0); // 没有在调整期间，返回空列表
            }
        }

        // 如果列表中存在调整的开始或结束标记，按原有逻辑处理
        bool isAdjustmentStarted = false;
        List<LogAbstract> currentAdjustment = new List<LogAbstract>();
        foreach (var logAbstract in sortedLogAbstracts)
        {
            if (logAbstract.Event == LogAbstractEventType.VoyageAdjustmentStart)
            {
                currentAdjustment.Clear();
                currentAdjustment.Add(logAbstract);
                isAdjustmentStarted = true;
            }
            else if (logAbstract.Event == LogAbstractEventType.VoyageAdjustmentOver)
            {
                if (isAdjustmentStarted)
                {
                    currentAdjustment.Add(logAbstract);
                    adjustLogAbstracts.AddRange(currentAdjustment);
                    currentAdjustment.Clear();
                    isAdjustmentStarted = false;
                }
            }
            else if (isAdjustmentStarted)
            {
                currentAdjustment.Add(logAbstract);
            }
        }

        return adjustLogAbstracts;
    }


    // /// <summary>
    // /// 获取 voyages 中所有 STS 航次的 log abstracts
    // /// </summary>
    // /// <param name="voyages"></param>
    // /// <returns></returns>
    // public static List<LogAbstract> GetStsLogAbstracts(List<Voyage> voyages)
    // {
    //     var stsVoyages = voyages.Where(v => v.IsSTS).ToList();
    //     if (stsVoyages.Count == 0)
    //     {
    //         return [];
    //     }
    //
    //     return stsVoyages.SelectMany(v => v.LogAbstracts).ToList();
    // }
}